package elastic.log.httpclientasync;

import com.alibaba.fastjson.JSON;

import elastic.log.SearchLog;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.nio.conn.NoopIOSessionStrategy;
import org.apache.http.nio.conn.SchemeIOSessionStrategy;
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.apache.http.nio.reactor.ConnectingIOReactor;
import org.apache.http.nio.reactor.IOReactorException;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.ssl.TrustStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @param <T> 泛型
 * @author quxb
 * @version 1.0
 * @project 深圳OA
 * @package zhbg.oaxt.httpclient
 * @file SearchDao.java 创建时间:2018年7月23日下午3:51:07
 * @title 标题（要求能简洁地表达出类的功能和职责）
 * @description 描述（简要描述类的职责、实现方式、使用注意事项等）
 * @copyright Copyright (c) 2018 中国软件与技术服务股份有限公司
 * @company 中国软件与技术服务股份有限公司
 * @module 模块: 模块名称
 * @reviewer 审核人
 * @history 修订历史（历次修订内容、修订人、修订时间等）
 */
public class AsyncHttpProxy<T> {

    /**
     * 最大重试次数
     */
    public static int maxRetryTimes = 3;

    final private String max_total = "200";
    final private String max_per_route = "200";
    final private String request_timeout = "30000";
    final private String socket_timeout = "30000";
    final private String connect_timeout = "20000";

    /**
     * 日志
     */
    private static Logger log = LoggerFactory.getLogger(AsyncHttpProxy.class);

    /**
     * 单例
     */
    @SuppressWarnings("unchecked")
    private static AsyncHttpProxy proxy = new AsyncHttpProxy();

    /**
     * 单例对象
     */
    private CloseableHttpAsyncClient asyncHttpClient;

    /**
     * 构造方法
     */
    private AsyncHttpProxy() {
        
        // 初始化连接池配置
        SchemeIOSessionStrategy schemeIOSessionStrategy = null;
        try {
            final SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();
            schemeIOSessionStrategy = new SSLIOSessionStrategy(sslContext, new String[]{"TLSv1", "TLSv1.2"}, null, NoopHostnameVerifier.INSTANCE);
        } catch (Exception e) {
            log.info("======初始化HttpAsyncClients连接池，SSL认证失败======", e);
        }
        final Registry<SchemeIOSessionStrategy> registry = RegistryBuilder.<SchemeIOSessionStrategy>create()
                .register("http", NoopIOSessionStrategy.INSTANCE)
                .register("https", schemeIOSessionStrategy)
                .build();

        final RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(Integer.parseInt(request_timeout))
                .setSocketTimeout(Integer.parseInt(socket_timeout))
                .setConnectTimeout(Integer.parseInt(connect_timeout))
                .build();

        final IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
                .setIoThreadCount(Runtime.getRuntime().availableProcessors()).build();
        ConnectingIOReactor ioReactor = null;
        try {
            ioReactor = new DefaultConnectingIOReactor(ioReactorConfig);
        } catch (IOReactorException e) {
            log.info("======初始化HttpAsyncClients连接池，创建DefaultConnectingIOReactor时失败======", e);
        }

        final PoolingNHttpClientConnectionManager connManager = new PoolingNHttpClientConnectionManager(ioReactor, registry);
        connManager.setMaxTotal(Integer.parseInt(max_total));
        connManager.setDefaultMaxPerRoute(Integer.parseInt(max_per_route));

        asyncHttpClient = HttpAsyncClients.custom()
                .setConnectionManager(connManager)
                .setDefaultRequestConfig(requestConfig).build();
        asyncHttpClient.start();
    }

    /**
     * @return 实例对象
     */
    @SuppressWarnings("unchecked")
    public static AsyncHttpProxy getAsyncHttpProxy() {
        return proxy;
    }

//    /**
//     * 发起post请求
//     *
//     * @param url      url
//     * @param searchVo 参数
//     * @param retryNo 重试次数
//     */
//    public void httpPost(String url, T searchVo, int retryNo) {
//        final HttpPost httpPost = setHttpPostParam(url, searchVo);
//        asyncHttpClient.execute(httpPost, new AsyncHttpCallback(url, searchVo, retryNo));
//    }

    /**
     * @param operation operation
     * @param url       url
     * @param id        id
     * @param index     index
     * @param searchVo  参数
     * @param retryNo   重试次数
     */
    public void httpPost(String operation, String url, String index, String id, T searchVo, int retryNo) {
        final List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
        final String value = JSON.toJSONString(searchVo);
        nameValuePairs.add(new BasicNameValuePair("index", index));
        nameValuePairs.add(new BasicNameValuePair("id", id));
        nameValuePairs.add(new BasicNameValuePair("value", value));

        final HttpPost httpPost = new HttpPost(url);
        // 设置参数
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "utf-8"));
        } catch (UnsupportedEncodingException e) {
            log.info("======发送async http post请求，转化编码失败======" + url, e);
        }
//        SearchLog.postLog(operation, index, id, value, null, retryNo, "szoa", "AsyncHttpProxy", true);
        
        asyncHttpClient.execute(httpPost, new AsyncHttpCallback(operation, url, index, id, value, retryNo));
    }

    /**
     * es对账，发送请求
     *
     * @param url          url
     * @param operation    操作类型
     * @param serviceType 业务类型
     * @param date         日期
     */
    public void httpPost(final String url, final String operation, final String serviceType, final String date) {
        final List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
        nameValuePairs.add(new BasicNameValuePair("operation", operation));
        nameValuePairs.add(new BasicNameValuePair("service_type", serviceType));
        nameValuePairs.add(new BasicNameValuePair("date", date));

        final HttpPost httpPost = new HttpPost(url);
        httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, StandardCharsets.UTF_8));

        // 回调不处理
        asyncHttpClient.execute(httpPost, new FutureCallback<HttpResponse>() {

            @Override
            public void completed(HttpResponse response) {

            }

            @Override
            public void failed(Exception e) {
                log.info("====es对账，请求失败====url====" + url + "====operation====" + operation + "====service_type====" + serviceType + "====date====" +date);
            }

            @Override
            public void cancelled() {

            }
        });
    }
    
    public void httpPostElk(final String url, final String searchVo, boolean bulk, FutureCallback callback) {
        final HttpPost httpPost = new HttpPost(url);

        // 设置参数
        try{
            final StringEntity entity = new StringEntity(searchVo, "utf-8");
            httpPost.setEntity(entity);
            httpPost.setHeader("Content-Type", "application/json; charset=UTF-8");
    //        httpPost.setHeader("Connection", "keep-alive");
    //        httpPost.setHeader("X-Requested-With", "XMLHttpRequest");
            
            asyncHttpClient.execute(httpPost, callback);
        }
        finally{
      //      httpPost.releaseConnection();
        }
    }
    
//    /**
//     * httpPost 设置参数
//     *
//     * @param url   url
//     * @param param param
//     * @return httpPost
//     */
//    private HttpPost setHttpPostParam(String url, Object param) {
//        final HttpPost httpPost = new HttpPost(url);
//
//        // 设置参数
//        final String paramStr = JSON.toJSONString(param);
//        final StringEntity postEntity = new StringEntity(paramStr, "UTF-8");
//        postEntity.setContentType("application/json");
//        httpPost.setEntity(postEntity);
//
//        return httpPost;
//    }

}

